package org.opens.javaskill.design;

import cn.hutool.core.collection.CollectionUtil;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 功能:
 *      过滤器模式的demo.
 */
public class FilterPattern {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person(1, "s1", 12, "男"));
        personList.add(new Person(2, "s2", 22, "女"));
        personList.add(new Person(3, "s3", 22, "男"));
        personList.add(new Person(4, "s4", 22, "女"));
        personList.add(new Person(5, "s5", 32, "男"));
        personList.add(new Person(6, "s6", 42, "女"));
        personList.add(new Person(7, "s7", 52, "女"));
        personList.add(new Person(8, "s8", 62, "男"));
        personList.add(new Person(9, "s9", 72, "男"));

        AndCriteria andCriteria = new AndCriteria(new CriteriaMale(), new CriteriaMajor());
        OrCriteria orCriteria = new OrCriteria(new CriteriaMale(), new CriteriaMajor());

        List<Person> personList1 = andCriteria.meetCriteria(personList);
        List<Person> personList2 = orCriteria.meetCriteria(personList);

        System.out.println(personList1);
        System.out.println(personList2);

        AndCriteria andCriteria1 = new AndCriteria(orCriteria, new CriteriaFemale());
        List<Person> personList3 = andCriteria1.meetCriteria(personList);
        System.out.println(personList3);
    }

}

/**
 * 说明:
 *      1. 被过滤的对象.
 */
@Data
@AllArgsConstructor
class Person {

    private Integer id;

    private String name;

    private Integer age;

    private String sex;

}

/**
 * 说明:
 *      1. 过滤器顶层接口.
 */
interface Criteria {

    List<Person> meetCriteria(List<Person> persons);

}

/**
 * 功能:
 *      筛选出男性.
 */
class CriteriaMale implements Criteria {

    @Override
    public List<Person> meetCriteria(List<Person> persons) {
        if ( CollectionUtil.isEmpty(persons) ) {
            return persons;
        }
        return persons.stream().filter(x -> x.getSex().equals("男")).collect(Collectors.toList());
    }

}

/**
 * 功能:
 *      1. 筛选出成年人.
 */
class CriteriaMajor implements Criteria {

    @Override
    public List<Person> meetCriteria(List<Person> persons) {
        if ( CollectionUtil.isEmpty(persons) ) {
            return persons;
        }
        return persons.stream().filter(x -> x.getAge() >= 18).collect(Collectors.toList());
    }

}

/**
 * 功能:
 *      1. 筛选出女性.
 */
class CriteriaFemale implements Criteria {

    @Override
    public List<Person> meetCriteria(List<Person> persons) {
        if ( CollectionUtil.isEmpty(persons) ) {
            return persons;
        }
        return persons.stream().filter(x -> x.getSex().equals("女")).collect(Collectors.toList());
    }

}

/**
 * 说明:
 *      1. 将筛选条件以and形式连接.
 *      2. 这里设计的非常巧妙就是: 这个类也实现了Criteria接口, 也可以再次被添加到and或者or进行组合, 就像递归一样, 设计很巧妙.
 */
@Data
class AndCriteria implements Criteria {

    private Criteria criteria;

    private Criteria otherCriteria;

    public AndCriteria(Criteria criteria, Criteria otherCriteria) {
        this.criteria = criteria;
        this.otherCriteria = otherCriteria;
    }

    @Override
    public List<Person> meetCriteria(List<Person> persons) {
        List<Person> people = criteria.meetCriteria(persons);
        return otherCriteria.meetCriteria(people);
    }

}

/**
 * 说明:
 *      1. 将筛选条件以or形式连接.
 *      2. 这里设计的非常巧妙就是: 这个类也实现了Criteria接口, 也可以再次被添加到and或者or进行组合, 就像递归一样, 设计很巧妙.
 */
@Data
class OrCriteria implements Criteria {

    private Criteria criteria;

    private Criteria otherCriteria;

    public OrCriteria(Criteria criteria, Criteria otherCriteria) {
        this.criteria = criteria;
        this.otherCriteria = otherCriteria;
    }

    @Override
    public List<Person> meetCriteria(List<Person> persons) {
        List<Person> people = criteria.meetCriteria(persons);
        List<Person> people1 = otherCriteria.meetCriteria(persons);

        for (Person person : people1) {
            if ( !people.contains(person) ) {
                people.add(person);
            }
        }

        return people;
    }

}


